package com.health.es.generate;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.health.es.annotation.FieldGenerate;
import com.health.es.enums.GenerateWay;
import com.health.es.temp.DictionaryUtil;
import com.health.es.temp.EsHelper;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.elasticsearch.action.update.UpdateRequest;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author nqlz
 */
@Slf4j
@Component
public class GenerateExecute<T> {

    public void run(Collection<?> objects, Class<T> tClass) {
        List<T> tList = objects.parallelStream()
                .map(t -> executeEntity(t, tClass))
                .collect(Collectors.toList());
        EsHelper.operations.save(tList);
    }


    @Getter
    protected List<Class<?>> tClasses;

    /**
     * 初始化类
     */
    @PostConstruct
    protected void initClasses() {
        addClasses(Object.class);
    }

    protected void addClasses(Class<?>... classes) {
        tClasses = Arrays.asList(classes);
    }

    private static <R> void executeField(R t, JSONObject json, Field i) {
        FieldGenerate fieldGenerate = i.getAnnotation(FieldGenerate.class);
        GenerateWay generateWay = fieldGenerate.generateEnum();
        String[] keys = ArrayUtils.addAll(generateWay.getKeys(), fieldGenerate.keys());
        boolean b = Arrays.stream(keys).anyMatch(j -> StrUtil.isBlank(json.getString(j)));
        if (b) {
            return;
        }
        if (generateWay.equals(GenerateWay.calculateAge)) {
            calculateAge(fieldGenerate, json, i.getName());
            return;
        }
        String value = GenerateCodeUtil.enCodeByDelimiter(json, t.getClass().getSimpleName(), keys);
        json.put(i.getName(), value);
    }

    private static void calculateAge(FieldGenerate fieldGenerate, JSONObject json, String key) {
        String[] primeKeys = fieldGenerate.generateEnum().getKeys();
        String[] customKeys = fieldGenerate.keys();
        Function<String, Date> getDate = s -> {
            try {
                Date date = json.getDate(key);
                return Objects.nonNull(date) ? date : DateUtil.parseDate(json.getString(s));
            } catch (Exception e) {
                return Convert.toDate(json.get(s));
            }
        };
        //支持 Date,LocalDate,LocalDateTime,不支持 ZonedDateTime
        Date dateStr = customKeys.length > 0 ? getDate.apply(customKeys[0]) : getDate.apply(primeKeys[0]);
        Duration between = LocalDateTimeUtil.between(LocalDateTimeUtil.of(dateStr), LocalDateTime.now());
        json.put(key, Math.max(between.toDays() / 365, 1));
    }

    public static <R, T> T executeEntity(R t, Class<T> clazz) {
        JSONObject json = DictionaryUtil.resolveJson(t, clazz, null);

        Arrays.stream(clazz.getDeclaredFields())
                .filter(i -> i.isAnnotationPresent(FieldGenerate.class))
                .forEach(i -> executeField(t, json, i));

        return JSONUtil.toBean(json.toJSONString(), clazz);
    }

    public static UpdateRequest getUpdateRequest(String indexName, String idString, Map<String, Object> source) {
        return new UpdateRequest(indexName, idString).doc(source);
    }
}
